#pragma once
#include "Header.h"
#include "CStrMgr.h"
#include "CStrTraits.h"

//
//  CStringT
//

template <typename _T, class CrtTraits>
class CStringT
{
    using XCHAR = typename CStrTraits<_T>::XCHAR;
    using PXSTR = typename CStrTraits<_T>::PXSTR;
    using PCXSTR = typename CStrTraits<_T>::PCXSTR;
    using YCHAR = typename CStrTraits<_T>::YCHAR;
    using PYSTR = typename CStrTraits<_T>::PYSTR;
    using PCYSTR = typename CStrTraits<_T>::PCYSTR;

private:
    PXSTR m_lpszData;

public:
    CStringT();
    CStringT(IN PCXSTR psz);
    CStringT(IN PCYSTR psz);
    CStringT(IN const CStringT& str);
    ~CStringT();

public:
    operator PCXSTR() const;
    operator PXSTR() const;

    CStringT& operator=(IN PCXSTR psz);
    CStringT& operator=(IN const CStringT& str);
    CStringT& operator=(IN PCYSTR psz);

    CStringT& operator+=(IN const CStringT& str);
    CStringT& operator+=(IN PCXSTR psz);
    CStringT& operator+=(IN PCYSTR psz);
    CStringT& operator+=(IN XCHAR ch);

    template<typename _T, class CrtTraits>
    friend CStringT operator+(IN const CStringT& str1, IN const CStringT& str2);
    template<typename _T, class CrtTraits>
    friend CStringT operator+(IN const CStringT& str1, IN PCXSTR psz2);
    template<typename _T, class CrtTraits>
    friend CStringT operator+(IN PCXSTR psz1, IN const CStringT& str2);

    template<typename _T, class CrtTraits>
    friend bool operator==(IN const CStringT& str1, IN const CStringT& str2);
    template<typename _T, class CrtTraits>
    friend bool operator==(IN const CStringT& str1, IN PCXSTR psz2);
    template<typename _T, class CrtTraits>
    friend bool operator==(IN const CStringT& str1, IN PCYSTR psz2);
    template<typename _T, class CrtTraits>
    friend bool operator==(IN PCXSTR psz1, IN const CStringT& str2);
    template<typename _T, class CrtTraits>
    friend bool operator==(IN PCYSTR psz1, IN const CStringT& str2);

    template<typename _T, class CrtTraits>
    friend bool operator!=(IN const CStringT& str1, IN const CStringT& str2);
    template<typename _T, class CrtTraits>
    friend bool operator!=(IN const CStringT& str1, IN PCXSTR psz2);
    template<typename _T, class CrtTraits>
    friend bool operator!=(IN const CStringT& str1, IN PCYSTR psz2);
    template<typename _T, class CrtTraits>
    friend bool operator!=(IN PCXSTR psz1, IN const CStringT& str2);
    template<typename _T, class CrtTraits>
    friend bool operator!=(IN PCYSTR psz1, IN const CStringT& str2);

    template<typename _T, class CrtTraits>
    friend bool operator<(IN const CStringT& str1, IN const CStringT& str2);
    template<typename _T, class CrtTraits>
    friend bool operator<(IN const CStringT& str1, IN PCXSTR psz2);
    template<typename _T, class CrtTraits>
    friend bool operator<(IN PCXSTR psz1, IN const CStringT& str2);

    template<typename _T, class CrtTraits>
    friend bool operator>(IN const CStringT& str1, IN const CStringT& str2);
    template<typename _T, class CrtTraits>
    friend bool operator>(IN const CStringT& str1, IN PCXSTR psz2);
    template<typename _T, class CrtTraits>
    friend bool operator>(IN PCXSTR psz1, IN const CStringT& str2);

    template<typename _T, class CrtTraits>
    friend bool operator<=(IN const CStringT& str1, IN const CStringT& str2);
    template<typename _T, class CrtTraits>
    friend bool operator<=(IN const CStringT& str1, IN PCXSTR psz2);
    template<typename _T, class CrtTraits>
    friend bool operator<=(IN PCXSTR psz1, IN const CStringT& str2);

    template<typename _T, class CrtTraits>
    friend bool operator>=(IN const CStringT& str1, IN const CStringT& str2);
    template<typename _T, class CrtTraits>
    friend bool operator>=(IN const CStringT& str1, IN PCXSTR psz2);
    template<typename _T, class CrtTraits>
    friend bool operator>=(IN PCXSTR psz1, IN const CStringT& str2);

    XCHAR operator[](IN int nIndex) const;

private:
    IStrMgr* GetStrMgr();
    void CopyChars(OUT CStrData* lpDstStrData, IN CStrData* lpSrcStrData);
    void CopyChars(OUT CStrData* lpDstStrData, IN PCXSTR lpSrcStr);
    void CopyChars(OUT CStrData* lpDstStrData, IN PCXSTR lpSrcStr, IN unsigned int length);
    void CopyChars(OUT CStrData* lpDstStrData, IN unsigned int nOffset, IN PCXSTR lpSrcStr, IN unsigned int length);
    int GetStrLength(IN const char* psz);
    int GetStrLength(IN const wchar_t* psz);
    void Attach(IN CStrData* lpStrData);
    void Detach();
    CStrData* GetCurStrData();
    BOOL IsNilStr();
    void SetStr(IN PCXSTR psz, IN int length);
    void SetStr(IN PCYSTR psz);
    void SetStr(IN PCYSTR psz, IN int length);
    void Append(IN PCXSTR psz, IN int length);
    void FormatV(IN PCXSTR pszFormat, IN va_list args);
    void AppendFormatV(IN PCXSTR pszFormat, IN va_list args);

public:
    void Empty();
    BOOL IsEmpty();
    PXSTR GetBuffer();
    void ReleaseBuffer(IN PXSTR lpStrBuff);
    void SetStr(IN PCXSTR psz);
    void Append(IN const CStringT& str);
    void Append(IN PCXSTR psz);
    void AppendChar(IN XCHAR ch);
    PCXSTR GetString() const;
    int GetLength();
    void SetAt(IN int nIndex, IN XCHAR ch);
    XCHAR GetAt(IN int nIndex);
    void Format(IN PCXSTR pszFormat, ...);
    void AppendFormat(IN PCXSTR pszFormat, ...);
    int Compare(IN PCXSTR psz);
    int CompareNoCase(IN PCXSTR psz);
    int Delete(IN int nIndex, IN int nCount = 1);
    int DeleteRight(IN int nIndex);
    int DeleteLeft(IN int nIndex);
    CStringT& MakeLower();
    CStringT& MakeUpper();
    CStringT& MakeReverse();
    void Left(IN int nEndIndex, OUT CStringT& str);
    void Right(IN int nStartIndex, OUT CStringT& str);
    void Mid(IN int nStartIndex, IN int nCount, OUT CStringT& str);
    int Find(IN PCXSTR pszSub, IN int nStartIndex = 0);
    int Find(IN XCHAR ch, IN int nStartIndex = 0);
    int FindOneOf(IN PCXSTR pszCharSet);
    int ReverseFind(IN XCHAR ch);
    int Insert(IN int nIndex, IN PCXSTR psz);
    int Insert(IN int nIndex, IN XCHAR ch);
    int Remove(IN XCHAR ch);
    int Replace(IN PCXSTR pszOld, IN PCXSTR pszNew);
    int Replace(IN XCHAR chOld, IN XCHAR chNew);
    void TrimLeft();
    void TrimLeft(IN XCHAR ch);
    void TrimLeft(IN PCXSTR psz);
    void TrimRight();
    void TrimRight(IN XCHAR ch);
    void TrimRight(IN PCXSTR psz);
    void Trim();
    void Trim(IN XCHAR ch);
    void Trim(IN PCXSTR psz);
};

template<typename _T, class CrtTraits>
inline CStringT<_T, CrtTraits>::CStringT()
{
    CStrData* lpNilStrData = CStrMgr::GetInstance()->GetNilStrData();
    Attach(lpNilStrData);
}

template<typename _T, class CrtTraits>
inline CStringT<_T, CrtTraits>::CStringT(IN PCXSTR psz) : CStringT()
{
    *this = psz;
}

template<typename _T, class CrtTraits>
inline CStringT<_T, CrtTraits>::CStringT(IN PCYSTR psz) : CStringT()
{
    *this = psz;
}

template<typename _T, class CrtTraits>
inline CStringT<_T, CrtTraits>::CStringT(IN const CStringT& str) : CStringT()
{
    *this = str.m_lpszData;
}

template<typename _T, class CrtTraits>
inline CStringT<_T, CrtTraits>::~CStringT()
{
    if (!IsNilStr())
        GetCurStrData()->Release();
}

template<typename _T, class CrtTraits>
inline CStringT<_T, CrtTraits>::operator PCXSTR() const
{
    return m_lpszData;
}

template<typename _T, class CrtTraits>
inline CStringT<_T, CrtTraits>::operator PXSTR() const
{
    return m_lpszData;
}

template<typename _T, class CrtTraits>
inline CStringT<_T, CrtTraits>& CStringT<_T, CrtTraits>::operator=(IN PCXSTR psz)
{
    SetStr(psz);
    return *this;
}

template<typename _T, class CrtTraits>
inline CStringT<_T, CrtTraits>& CStringT<_T, CrtTraits>::operator=(IN const CStringT& str)
{
    *this = str.m_lpszData;
    return *this;
}

template<typename _T, class CrtTraits>
inline CStringT<_T, CrtTraits>& CStringT<_T, CrtTraits>::operator=(IN PCYSTR psz)
{
    SetStr(psz);
    return *this;
}

template<typename _T, class CrtTraits>
inline CStringT<_T, CrtTraits>& CStringT<_T, CrtTraits>::operator+=(IN const CStringT& str)
{
    Append(str);
    return *this;
}

template<typename _T, class CrtTraits>
inline CStringT<_T, CrtTraits>& CStringT<_T, CrtTraits>::operator+=(IN PCXSTR psz)
{
    Append(psz);
    return *this;
}

template<typename _T, class CrtTraits>
inline CStringT<_T, CrtTraits>& CStringT<_T, CrtTraits>::operator+=(IN PCYSTR psz)
{
    CStringT str = psz;
    Append(str);
    return *this;
}

template<typename _T, class CrtTraits>
inline CStringT<_T, CrtTraits>& CStringT<_T, CrtTraits>::operator+=(IN XCHAR ch)
{
    AppendChar(ch);
    return *this;
}

template<typename _T, class CrtTraits>
CStringT<_T, CrtTraits> operator+(IN const CStringT<_T, CrtTraits>& str1, IN const CStringT<_T, CrtTraits>& str2)
{
    CStringT<_T, CrtTraits> strTemp;
    strTemp = str1;
    strTemp.Append(str2);
    return strTemp;
}

template<typename _T, class CrtTraits>
CStringT<_T, CrtTraits> operator+(IN const CStringT<_T, CrtTraits>& str1, IN typename CStrTraits<_T>::PCXSTR psz2)
{
    CStringT<_T, CrtTraits> strTemp;
    strTemp = str1;
    strTemp.Append(psz2);
    return strTemp;
}

template<typename _T, class CrtTraits>
CStringT<_T, CrtTraits> operator+(IN typename CStrTraits<_T>::PCXSTR psz1, IN const CStringT<_T, CrtTraits>& str2)
{
    CStringT<_T, CrtTraits> strTemp;
    strTemp = psz1;
    strTemp.Append(str2);
    return strTemp;
}

template<typename _T, class CrtTraits>
bool operator==(IN const CStringT<_T, CrtTraits>& str1, IN const CStringT<_T, CrtTraits>& str2)
{
    int n = CCrtTraits<_T>::Compare(str1, str2);
    return ((n == 0) ? true : false);
}

template<typename _T, class CrtTraits>
bool operator==(IN const CStringT<_T, CrtTraits>& str1, IN typename CStrTraits<_T>::PCXSTR psz2)
{
    int n = CCrtTraits<_T>::Compare(str1, psz2);
    return ((n == 0) ? true : false);
}

template<typename _T, class CrtTraits>
bool operator==(IN const CStringT<_T, CrtTraits>& str1, IN typename CStrTraits<_T>::PCYSTR psz2)
{
    CStringT<_T, CrtTraits> str2 = psz2;
    int n = CCrtTraits<_T>::Compare(str1, str2);
    return ((n == 0) ? true : false);
}

template<typename _T, class CrtTraits>
bool operator==(IN typename CStrTraits<_T>::PCXSTR psz1, IN const CStringT<_T, CrtTraits>& str2)
{
    int n = CCrtTraits<_T>::Compare(psz1, str2);
    return ((n == 0) ? true : false);
}

template<typename _T, class CrtTraits>
bool operator==(IN typename CStrTraits<_T>::PCYSTR psz1, IN const CStringT<_T, CrtTraits>& str2)
{
    CStringT<_T, CrtTraits> str1 = psz1;
    int n = CCrtTraits<_T>::Compare(str1, str2);
    return ((n == 0) ? true : false);
}

template<typename _T, class CrtTraits>
bool operator!=(IN const CStringT<_T, CrtTraits>& str1, IN const CStringT<_T, CrtTraits>& str2)
{
    int n = CCrtTraits<_T>::Compare(str1, str2);
    return ((n == 0) ? false : true);
}

template<typename _T, class CrtTraits>
bool operator!=(IN const CStringT<_T, CrtTraits>& str1, IN typename CStrTraits<_T>::PCXSTR psz2)
{
    int n = CCrtTraits<_T>::Compare(str1, psz2);
    return ((n == 0) ? false : true);
}

template<typename _T, class CrtTraits>
bool operator!=(IN const CStringT<_T, CrtTraits>& str1, IN typename CStrTraits<_T>::PCYSTR psz2)
{
    CStringT<_T, CrtTraits> str2 = psz2;
    int n = CCrtTraits<_T>::Compare(str1, str2);
    return ((n == 0) ? false : true);
}

template<typename _T, class CrtTraits>
bool operator!=(IN typename CStrTraits<_T>::PCXSTR psz1, IN const CStringT<_T, CrtTraits>& str2)
{
    int n = CCrtTraits<_T>::Compare(psz1, str2);
    return ((n == 0) ? false : true);
}

template<typename _T, class CrtTraits>
bool operator!=(IN typename CStrTraits<_T>::PCYSTR psz1, IN const CStringT<_T, CrtTraits>& str2)
{
    CStringT<_T, CrtTraits> str1 = psz1;
    int n = CCrtTraits<_T>::Compare(str1, str2);
    return ((n == 0) ? false : true);
}

template<typename _T, class CrtTraits>
bool operator<(IN const CStringT<_T, CrtTraits>& str1, IN const CStringT<_T, CrtTraits>& str2)
{
    int n = CCrtTraits<_T>::Compare(str1, str2);
    return ((n < 0) ? true : false);
}

template<typename _T, class CrtTraits>
bool operator<(IN const CStringT<_T, CrtTraits>& str1, IN typename CStrTraits<_T>::PCXSTR psz2)
{
    CStringT<_T, CrtTraits> str2 = psz2;
    int n = CCrtTraits<_T>::Compare(str1, str2);
    return ((n < 0) ? true : false);
}

template<typename _T, class CrtTraits>
bool operator<(IN typename CStrTraits<_T>::PCXSTR psz1, IN const CStringT<_T, CrtTraits>& str2)
{
    CStringT<_T, CrtTraits> str1 = psz1;
    int n = CCrtTraits<_T>::Compare(str1, str2);
    return ((n < 0) ? true : false);
}

template<typename _T, class CrtTraits>
bool operator>(IN const CStringT<_T, CrtTraits>& str1, IN const CStringT<_T, CrtTraits>& str2)
{
    int n = CCrtTraits<_T>::Compare(str1, str2);
    return ((n > 0) ? true : false);
}

template<typename _T, class CrtTraits>
bool operator>(IN const CStringT<_T, CrtTraits>& str1, IN typename CStrTraits<_T>::PCXSTR psz2)
{
    CStringT<_T, CrtTraits> str2 = psz2;
    int n = CCrtTraits<_T>::Compare(str1, str2);
    return ((n > 0) ? true : false);
}

template<typename _T, class CrtTraits>
bool operator>(IN typename CStrTraits<_T>::PCXSTR psz1, IN const CStringT<_T, CrtTraits>& str2)
{
    CStringT<_T, CrtTraits> str1 = psz1;
    int n = CCrtTraits<_T>::Compare(str1, str2);
    return ((n > 0) ? true : false);
}

template<typename _T, class CrtTraits>
bool operator<=(IN const CStringT<_T, CrtTraits>& str1, IN const CStringT<_T, CrtTraits>& str2)
{
    int n = CCrtTraits<_T>::Compare(str1, str2);
    return ((n <= 0) ? true : false);
}

template<typename _T, class CrtTraits>
bool operator<=(IN const CStringT<_T, CrtTraits>& str1, IN typename CStrTraits<_T>::PCXSTR psz2)
{
    CStringT<_T, CrtTraits> str2 = psz2;
    int n = CCrtTraits<_T>::Compare(str1, str2);
    return ((n <= 0) ? true : false);
}

template<typename _T, class CrtTraits>
bool operator<=(IN typename CStrTraits<_T>::PCXSTR psz1, IN const CStringT<_T, CrtTraits>& str2)
{
    CStringT<_T, CrtTraits> str1 = psz1;
    int n = CCrtTraits<_T>::Compare(str1, str2);
    return ((n <= 0) ? true : false);
}

template<typename _T, class CrtTraits>
bool operator>=(IN const CStringT<_T, CrtTraits>& str1, IN const CStringT<_T, CrtTraits>& str2)
{
    int n = CCrtTraits<_T>::Compare(str1, str2);
    return ((n >= 0) ? true : false);
}

template<typename _T, class CrtTraits>
bool operator>=(IN const CStringT<_T, CrtTraits>& str1, IN typename CStrTraits<_T>::PCXSTR psz2)
{
    CStringT<_T, CrtTraits> str2 = psz2;
    int n = CCrtTraits<_T>::Compare(str1, str2);
    return ((n >= 0) ? true : false);
}

template<typename _T, class CrtTraits>
bool operator>=(IN typename CStrTraits<_T>::PCXSTR psz1, IN const CStringT<_T, CrtTraits>& str2)
{
    CStringT<_T, CrtTraits> str1 = psz1;
    int n = CCrtTraits<_T>::Compare(str1, str2);
    return ((n >= 0) ? true : false);
}

template<typename _T, class CrtTraits>
inline typename CStringT<_T, CrtTraits>::XCHAR CStringT<_T, CrtTraits>::operator[](IN int nIndex) const
{
    return m_lpszData[nIndex];
}

template<typename _T, class CrtTraits>
inline IStrMgr* CStringT<_T, CrtTraits>::GetStrMgr()
{
    return GetCurStrData()->m_lpStrMgr;
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::CopyChars(OUT CStrData* lpDstStrData, IN CStrData* lpSrcStrData)
{
    if (!lpDstStrData || !lpSrcStrData) return;

    RtlCopyMemory(lpDstStrData->GetStr(), lpSrcStrData->GetStr(), lpSrcStrData->m_nDataLength * sizeof(XCHAR));
    lpDstStrData->m_nDataLength = lpSrcStrData->m_nDataLength;
    ((PXSTR)lpDstStrData->GetStr())[lpDstStrData->m_nDataLength] = 0;
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::CopyChars(OUT CStrData* lpDstStrData, IN PCXSTR lpSrcStr)
{
    if (!lpDstStrData || !lpSrcStr) return;

    int nlength = CrtTraits::GetStrLength(lpSrcStr);
    CopyChars(lpDstStrData, lpSrcStr, nlength);
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::CopyChars(OUT CStrData* lpDstStrData, IN PCXSTR lpSrcStr, IN unsigned int length)
{
    if (!lpDstStrData || !lpSrcStr) return;

    RtlCopyMemory(((PXSTR)lpDstStrData->GetStr()), lpSrcStr, length * sizeof(XCHAR));
    lpDstStrData->m_nDataLength = length;
    ((PXSTR)lpDstStrData->GetStr())[lpDstStrData->m_nDataLength] = 0;
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::CopyChars(OUT CStrData* lpDstStrData, IN unsigned int nOffset, IN PCXSTR lpSrcStr, IN unsigned int length)
{
    if (!lpDstStrData || !lpSrcStr) return;

    RtlCopyMemory(((PXSTR)lpDstStrData->GetStr()) + nOffset, lpSrcStr, length * sizeof(XCHAR));
    lpDstStrData->m_nDataLength = nOffset + length;
    ((PXSTR)lpDstStrData->GetStr())[lpDstStrData->m_nDataLength] = 0;
}

template<typename _T, class CrtTraits>
inline int CStringT<_T, CrtTraits>::GetStrLength(IN const char* psz)
{
    if (!psz)
        return 0;
    else
        return ((int)strlen(psz));
}

template<typename _T, class CrtTraits>
inline int CStringT<_T, CrtTraits>::GetStrLength(IN const wchar_t* psz)
{
    if (!psz)
        return 0;
    else
        return ((int)wcslen(psz));
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::Attach(IN CStrData* lpStrData)
{
    m_lpszData = static_cast<PXSTR>(lpStrData->GetStr());
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::Detach()
{
    Empty();
}

template<typename _T, class CrtTraits>
inline CStrData* CStringT<_T, CrtTraits>::GetCurStrData()
{
    return (reinterpret_cast<CStrData*>(m_lpszData) - 1);
}

template<typename _T, class CrtTraits>
inline BOOL CStringT<_T, CrtTraits>::IsNilStr()
{
    if (m_lpszData == CStrMgr::GetInstance()->GetNilStrData()->GetStr())
        return TRUE;
    else
        return FALSE;
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::SetStr(IN PCXSTR psz, IN int length)
{
    if (!length)
    {
        Detach();
        return;
    }

    CStrData* lpNewStrData;
    int nBytes = length * sizeof(XCHAR) + sizeof(XCHAR);
    int nLimit = GetCurStrData()->m_nAllocLength - sizeof(XCHAR);

    if (IsNilStr())
    {
        lpNewStrData = GetStrMgr()->Allocate(length, sizeof(XCHAR));
        Attach(lpNewStrData);
    }
    else if ((nLimit < nBytes) | (nLimit > nBytes * 2))
    {
        lpNewStrData = GetStrMgr()->Allocate(length, sizeof(XCHAR));
        GetCurStrData()->Release();
        Attach(lpNewStrData);
    }

    RtlCopyMemory(m_lpszData, psz, length * sizeof(XCHAR));
    m_lpszData[length] = 0;
    GetCurStrData()->m_nDataLength = length;
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::SetStr(IN PCYSTR psz)
{
    SetStr(psz, CrtTraits::GetStrLength(psz));
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::SetStr(IN PCYSTR psz, IN int length)
{
    CStrData* lpNewStrData;

    if (!length)
    {
        Detach();
        return;
    }

    lpNewStrData = GetCurStrData()->m_lpStrMgr->Allocate(length, sizeof(XCHAR));
    if (!lpNewStrData) return;

    CrtTraits::ConvertToBaseType((PXSTR)lpNewStrData->GetStr(), length, psz);
    if (!IsNilStr())
        GetCurStrData()->Release();
    Attach(lpNewStrData);
    m_lpszData[length] = 0;
    GetCurStrData()->m_nDataLength = length;
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::Append(IN PCXSTR psz, IN int length)
{
    if (!length) return;

    CStrData* lpCurStrData = GetCurStrData();
    CStrData* lpNewStrData;
    int nlength = length * sizeof(XCHAR);
    int nTotal = nlength + lpCurStrData->m_nDataLength * sizeof(XCHAR);
    int nLimit = lpCurStrData->m_nAllocLength - sizeof(XCHAR);

    if (nTotal < nLimit)
    {
        CopyChars(lpCurStrData, lpCurStrData->m_nDataLength, psz, length);
        return;
    }

    lpNewStrData = GetStrMgr()->Allocate(length + lpCurStrData->m_nDataLength, sizeof(XCHAR));
    if (!lpNewStrData) return;

    CopyChars(lpNewStrData, lpCurStrData);
    CopyChars(lpNewStrData, lpNewStrData->m_nDataLength, psz, length);

    if (!IsNilStr())
        lpCurStrData->Release();

    Attach(lpNewStrData);
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::FormatV(IN PCXSTR pszFormat, IN va_list args)
{
    if (!pszFormat) return;

    int nlength = CrtTraits::GetFormatLength(pszFormat, args);
    CStrData* lpNewStrData = GetStrMgr()->Allocate(nlength, sizeof(XCHAR));
    if (!lpNewStrData) return;

    CStringT strTemp(pszFormat);
    CrtTraits::Format((PXSTR)lpNewStrData->GetStr(), nlength + 1, strTemp, args);
    lpNewStrData->m_nDataLength = nlength;

    if (!IsNilStr())
        GetCurStrData()->Release();

    Attach(lpNewStrData);
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::AppendFormatV(IN PCXSTR pszFormat, IN va_list args)
{
    if (!pszFormat) return;

    int nlength = CrtTraits::GetFormatLength(pszFormat, args);
    CStrData* lpNewStrData = GetStrMgr()->Allocate(nlength + GetCurStrData()->m_nDataLength, sizeof(XCHAR));
    if (!lpNewStrData) return;

    CopyChars(lpNewStrData, GetCurStrData());

    CStringT strTemp(pszFormat);
    CrtTraits::Format((PXSTR)lpNewStrData->GetStr() + GetCurStrData()->m_nDataLength, nlength + 1, strTemp, args);
    lpNewStrData->m_nDataLength = nlength + GetCurStrData()->m_nDataLength;

    if (!IsNilStr())
        GetCurStrData()->Release();

    Attach(lpNewStrData);
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::Empty()
{
    CStrData* lpStrData = GetCurStrData();
    IStrMgr* lpStrMgr = lpStrData->m_lpStrMgr;

    if (!IsNilStr())
    {
        lpStrData->Release();
        Attach(lpStrMgr->GetNilStrData());
    }
}

template<typename _T, class CrtTraits>
inline BOOL CStringT<_T, CrtTraits>::IsEmpty()
{
    CStrData* lpStrData = GetCurStrData();

    if (lpStrData->m_nDataLength)
        return FALSE;
    else
        return TRUE;
}

template<typename _T, class CrtTraits>
inline typename CStringT<_T, CrtTraits>::PXSTR CStringT<_T, CrtTraits>::GetBuffer()
{
    CStrData* lpNewStrData;

    lpNewStrData = CStrMgr::GetInstance()->Allocate(GetCurStrData()->m_nDataLength, sizeof(XCHAR));
    if (!lpNewStrData) return nullptr;

    CopyChars(lpNewStrData, GetCurStrData());

    return ((PXSTR)(lpNewStrData + 1));
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::ReleaseBuffer(IN PXSTR lpStrBuff)
{
    if (!lpStrBuff) return;

    CStrData* lpStrData = (CStrData*)lpStrBuff - 1;
    lpStrData->Release();
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::SetStr(IN PCXSTR psz)
{
    SetStr(psz, GetStrLength(psz));
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::Append(IN const CStringT& str)
{
    Append(str.m_lpszData, GetStrLength(str.m_lpszData));
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::Append(IN PCXSTR psz)
{
    Append(psz, GetStrLength(psz));
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::AppendChar(IN XCHAR ch)
{
    CStrData* lpCurStrData = GetCurStrData();
    CStrData* lpNewStrData;
    IStrMgr* lpStrMgr = lpCurStrData->m_lpStrMgr;
    int nBytes = lpCurStrData->m_nDataLength * sizeof(XCHAR) + sizeof(XCHAR);
    int nLimit = lpCurStrData->m_nAllocLength - sizeof(XCHAR);

    if (nLimit > nBytes)
    {
        m_lpszData[lpCurStrData->m_nDataLength] = ch;
        lpCurStrData->m_nDataLength++;
        m_lpszData[lpCurStrData->m_nDataLength] = 0;
        return;
    }

    lpNewStrData = GetStrMgr()->Allocate(lpCurStrData->m_nDataLength + 1, sizeof(XCHAR));
    if (!lpNewStrData) return;

    CopyChars(lpNewStrData, lpCurStrData);
    ((PXSTR)lpNewStrData->GetStr())[lpCurStrData->m_nDataLength] = ch;
    lpNewStrData->m_nDataLength = lpCurStrData->m_nDataLength + 1;
    ((PXSTR)lpNewStrData->GetStr())[lpNewStrData->m_nDataLength] = 0;

    if (!IsNilStr())
        lpCurStrData->Release();

    Attach(lpNewStrData);
}

template<typename _T, class CrtTraits>
inline typename CStringT<_T, CrtTraits>::PCXSTR CStringT<_T, CrtTraits>::GetString() const
{
    return m_lpszData;
}

template<typename _T, class CrtTraits>
inline int CStringT<_T, CrtTraits>::GetLength()
{
    return GetCurStrData()->m_nDataLength;
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::SetAt(IN int nIndex, IN XCHAR ch)
{
    if (IsNilStr() || nIndex < 0 || nIndex > GetLength()) return;
    m_lpszData[nIndex] = ch;
}

template<typename _T, class CrtTraits>
inline typename CStringT<_T, CrtTraits>::XCHAR CStringT<_T, CrtTraits>::GetAt(IN int nIndex)
{
    return m_lpszData[nIndex];
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::Format(IN PCXSTR pszFormat, ...)
{
    va_list argList;
    va_start(argList, pszFormat);
    FormatV(pszFormat, argList);
    va_end(argList);
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::AppendFormat(IN PCXSTR pszFormat, ...)
{
    va_list argList;
    va_start(argList, pszFormat);
    AppendFormatV(pszFormat, argList);
    va_end(argList);
}

template<typename _T, class CrtTraits>
inline int CStringT<_T, CrtTraits>::Compare(IN PCXSTR psz)
{
    return CrtTraits::Compare(*this, psz);
}

template<typename _T, class CrtTraits>
inline int CStringT<_T, CrtTraits>::CompareNoCase(IN PCXSTR psz)
{
    return CrtTraits::CompareNoCase(*this, psz);
}

template<typename _T, class CrtTraits>
inline int CStringT<_T, CrtTraits>::Delete(IN int nIndex, IN int nCount)
{
    int nTotal;
    int nRest;

    if (IsNilStr() || !(GetCurStrData()->m_nDataLength)) return 0;

    if (nIndex < 0) nIndex = 0;
    if (nCount < 0) nCount = 0;

    if (nIndex + nCount >= GetCurStrData()->m_nDataLength)
    {
        m_lpszData[nIndex] = 0;
        GetCurStrData()->m_nDataLength = nIndex;
        return nIndex;
    }

    nRest = GetCurStrData()->m_nDataLength - nIndex - nCount;
    nTotal = nIndex + nRest;
    RtlCopyMemory(m_lpszData + nIndex, m_lpszData + nIndex + nCount, nRest * sizeof(XCHAR));
    m_lpszData[nTotal] = 0;
    GetCurStrData()->m_nDataLength = nTotal;

    return nTotal;
}

template<typename _T, class CrtTraits>
inline int CStringT<_T, CrtTraits>::DeleteRight(IN int nIndex)
{
    if (IsNilStr() || !(GetCurStrData()->m_nDataLength)) return 0;

    if (nIndex < 0) nIndex = 0;

    if (nIndex < GetCurStrData()->m_nDataLength)
    {
        m_lpszData[nIndex] = 0;
        GetCurStrData()->m_nDataLength = nIndex;
        return nIndex;
    }

    return 0;
}

template<typename _T, class CrtTraits>
inline int CStringT<_T, CrtTraits>::DeleteLeft(IN int nIndex)
{
    int nRest = 0;

    if (IsNilStr() || !(GetCurStrData()->m_nDataLength)) return 0;

    if (nIndex < 0) nIndex = 0;

    if (nIndex <= GetCurStrData()->m_nDataLength)
    {
        nRest = GetCurStrData()->m_nDataLength - nIndex;
        RtlCopyMemory(m_lpszData, m_lpszData + nIndex, nRest * sizeof(XCHAR));
        m_lpszData[nRest] = 0;
        GetCurStrData()->m_nDataLength = nRest;
    }

    return nRest;
}

template<typename _T, class CrtTraits>
inline CStringT<_T, CrtTraits>& CStringT<_T, CrtTraits>::MakeLower()
{
    if (!IsNilStr() || (GetCurStrData()->m_nDataLength))
        CrtTraits::Lowercase(m_lpszData, GetCurStrData()->m_nDataLength + 1);
    return *this;
}

template<typename _T, class CrtTraits>
inline CStringT<_T, CrtTraits>& CStringT<_T, CrtTraits>::MakeUpper()
{
    if (!IsNilStr() || (GetCurStrData()->m_nDataLength))
        CrtTraits::Uppercase(m_lpszData, GetCurStrData()->m_nDataLength + 1);
    return *this;
}

template<typename _T, class CrtTraits>
inline CStringT<_T, CrtTraits>& CStringT<_T, CrtTraits>::MakeReverse()
{
    if (!IsNilStr() || (GetCurStrData()->m_nDataLength))
        CrtTraits::Reverse(m_lpszData);
    return *this;
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::Left(IN int nEndIndex, OUT CStringT& str)
{
    str = *this;
    str.DeleteRight(nEndIndex);
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::Right(IN int nStartIndex, OUT CStringT& str)
{
    str = *this;
    str.DeleteLeft(nStartIndex);
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::Mid(IN int nStartIndex, IN int nCount, OUT CStringT& str)
{
    if (IsNilStr() || !GetLength() || nStartIndex < 0 || nStartIndex > GetLength()) return;

    if ((nStartIndex + nCount) > GetLength())
        nCount = GetLength() - nStartIndex;

    str = *this;
    str.DeleteRight(nStartIndex + nCount);
    str.DeleteLeft(nStartIndex);
}

template<typename _T, class CrtTraits>
inline int CStringT<_T, CrtTraits>::Find(IN PCXSTR pszSub, IN int nStartIndex)
{
    if (!pszSub || IsNilStr() || !GetLength() || nStartIndex < 0 || nStartIndex > GetLength()) return -1;

    PCXSTR lpStr = CrtTraits::FindSubStr(GetString() + nStartIndex, pszSub);
    return ((lpStr == nullptr) ? -1 : int(lpStr - GetString()));
}

template<typename _T, class CrtTraits>
inline int CStringT<_T, CrtTraits>::Find(IN XCHAR ch, IN int nStartIndex)
{
    if (IsNilStr() || !GetLength() || nStartIndex < 0 || nStartIndex > GetLength()) return -1;
    PCXSTR lpStr = CrtTraits::FindSubChr(GetString() + nStartIndex, ch);
    return ((lpStr == nullptr) ? -1 : int(lpStr - GetString()));
}

template<typename _T, class CrtTraits>
inline int CStringT<_T, CrtTraits>::FindOneOf(IN PCXSTR pszCharSet)
{
    if (IsNilStr() || !GetLength() || !pszCharSet) return -1;
    PCXSTR lpStr = CrtTraits::FindChrFromSet(GetString(), pszCharSet);
    return ((lpStr == nullptr) ? -1 : int(lpStr - GetString()));
}

template<typename _T, class CrtTraits>
inline int CStringT<_T, CrtTraits>::ReverseFind(IN XCHAR ch)
{
    if (IsNilStr() || !GetLength()) return -1;
    PCXSTR lpStr = CrtTraits::FindChrFromEnd(GetString(), ch);
    return ((lpStr == nullptr) ? -1 : int(lpStr - GetString()));
}

template<typename _T, class CrtTraits>
inline int CStringT<_T, CrtTraits>::Insert(IN int nIndex, IN PCXSTR psz)
{
    if (!psz || IsNilStr() || !GetLength() || nIndex < 0 || nIndex > GetLength()) return GetLength();

    if (nIndex == GetLength())
    {
        Append(psz);
    }
    else
    {
        CStringT str;
        Right(nIndex, str);
        DeleteRight(nIndex);
        Append(psz);
        Append(str);
    }

    return GetLength();
}

template<typename _T, class CrtTraits>
inline int CStringT<_T, CrtTraits>::Insert(IN int nIndex, IN XCHAR ch)
{
    if (IsNilStr() || !GetLength() || nIndex < 0 || nIndex > GetLength()) return GetLength();

    if (nIndex == GetLength())
    {
        AppendChar(ch);
    }
    else
    {
        CStringT str;
        Right(nIndex, str);
        DeleteRight(nIndex);
        AppendChar(ch);
        Append(str);
    }

    return GetLength();
}

template<typename _T, class CrtTraits>
inline int CStringT<_T, CrtTraits>::Remove(IN XCHAR ch)
{
    if (IsNilStr() || !GetLength()) return 0;

    PXSTR lpStrBuff = GetBuffer();
    if (!lpStrBuff) return 0;

    PXSTR lpSrcStr = lpStrBuff;
    PXSTR lpSrcStrEnd = lpStrBuff + GetLength();
    PXSTR lpDstStr = lpStrBuff;
    PXSTR lpNewSrcStr;
    PXSTR lpNewDstStr;
    size_t stGap;
    int nCount = 0;

    while (lpSrcStr < lpSrcStrEnd)
    {
        lpNewSrcStr = lpSrcStr + 1;
        if (*lpSrcStr != ch)
        {
            stGap = (lpNewSrcStr - lpSrcStr);
            lpNewDstStr = lpDstStr + stGap;
            for (size_t i = 0; lpDstStr != lpNewDstStr && i < stGap; i++)
            {
                *lpDstStr = *lpSrcStr;
                lpSrcStr++;
                lpDstStr++;
            }
        }
        lpSrcStr = lpNewSrcStr;
    }

    *lpDstStr = 0;
    nCount = (int)(lpSrcStr - lpDstStr);
    *this = lpStrBuff;
    ReleaseBuffer(lpStrBuff);

    return nCount;
}

template<typename _T, class CrtTraits>
inline int CStringT<_T, CrtTraits>::Replace(IN PCXSTR pszOld, IN PCXSTR pszNew)
{
    if (!pszOld || !pszNew || IsNilStr() || !GetLength()) return 0;

    int nLength = CrtTraits::GetStrLength(pszOld);
    int nCount = 0;
    int nIndex;
    CStringT str;

    while ((nIndex = Find(pszOld)) != -1)
    {
        Right(nIndex + nLength, str);
        DeleteRight(nIndex);
        Append(pszNew);
        Append(str);
        nCount++;
    }

    return nCount;
}

template<typename _T, class CrtTraits>
inline int CStringT<_T, CrtTraits>::Replace(IN XCHAR chOld, IN XCHAR chNew)
{
    if (IsNilStr() || !GetLength()) return 0;

    int nCount = 0;

    for (int i = 0; i < GetLength(); ++i)
    {
        if (m_lpszData[i] == chOld)
            m_lpszData[i] = chNew;
        nCount++;
    }

    return nCount;
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::TrimLeft()
{
    if (IsNilStr() || !GetLength()) return;

    int nLastIndex = 0;

    for (int i = GetLength() - 1; i >= 0; --i)
    {
        if (CrtTraits::IsSpace(m_lpszData[i]))
        {
            if (!nLastIndex)
                nLastIndex = i;
        }
        else
        {
            nLastIndex = 0;
        }
    }

    if (nLastIndex)
        DeleteLeft(nLastIndex + 1);
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::TrimLeft(IN XCHAR ch)
{
    if (IsNilStr() || !GetLength()) return;

    int nLastIndex = 0;

    for (int i = GetLength() - 1; i >= 0; --i)
    {
        if (ch == m_lpszData[i])
        {
            if (!nLastIndex)
                nLastIndex = i;
        }
        else
        {
            nLastIndex = 0;
        }
    }

    if (nLastIndex)
        DeleteLeft(nLastIndex + 1);
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::TrimLeft(IN PCXSTR psz)
{
    if (IsNilStr() || !GetLength()) return;

    int nLastIndex = 0;

    for (int i = GetLength() - 1; i >= 0; --i)
    {
        if (CrtTraits::FindSubChr(psz, m_lpszData[i]))
        {
            if (!nLastIndex)
                nLastIndex = i;
        }
        else
        {
            nLastIndex = 0;
        }
    }

    if (nLastIndex)
        DeleteLeft(nLastIndex + 1);
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::TrimRight()
{
    if (IsNilStr() || !GetLength()) return;

    int nLastIndex = 0;

    for (int i = 0; i < GetLength(); ++i)
    {
        if (CrtTraits::IsSpace(m_lpszData[i]))
        {
            if (!nLastIndex)
                nLastIndex = i;
        }
        else
        {
            nLastIndex = 0;
        }
    }

    if (nLastIndex)
        DeleteRight(nLastIndex);
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::TrimRight(IN XCHAR ch)
{
    if (IsNilStr() || !GetLength()) return;

    int nLastIndex = 0;

    for (int i = 0; i < GetLength(); ++i)
    {
        if (m_lpszData[i] == ch)
        {
            if (!nLastIndex)
                nLastIndex = i;
        }
        else
        {
            nLastIndex = 0;
        }
    }

    if (nLastIndex)
        DeleteRight(nLastIndex);
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::TrimRight(IN PCXSTR psz)
{
    if (IsNilStr() || !GetLength()) return;

    int nLastIndex = 0;

    for (int i = 0; i < GetLength(); ++i)
    {
        if (CrtTraits::FindSubChr(psz, m_lpszData[i]))
        {
            if (!nLastIndex)
                nLastIndex = i;
        }
        else
        {
            nLastIndex = 0;
        }
    }

    if (nLastIndex)
        DeleteRight(nLastIndex);
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::Trim()
{
    TrimRight();
    TrimLeft();
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::Trim(IN XCHAR ch)
{
    TrimRight(ch);
    TrimLeft(ch);
}

template<typename _T, class CrtTraits>
inline void CStringT<_T, CrtTraits>::Trim(IN PCXSTR psz)
{
    TrimRight(psz);
    TrimLeft(psz);
}


using CString = CStringT<TCHAR, CCrtTraits<TCHAR>>;
using CStringA = CStringT<CHAR, CCrtTraits<CHAR>>;
using CStringW = CStringT<WCHAR, CCrtTraits<WCHAR>>;
